/**
 * Copyright (c) 2009, Jacek Karczmarczyk
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * - Neither the name of the author nor the names of its contributors may be
 *   used to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * @package Larchana_Javascript
 * @version 0.1
 * @author Jacek Karczmarczyk <jacek@karczmarczyk.pl>
 */

"use strict";

/**
 *
 * Usage:
 *
 * Example 1:
 * ----------
 * <a href="/removelink" class="removeMe">Try to remove me!</a>
 * <script type="text/javascript">
 * $('a.removeMe').larchanaConfirmation({
 *     beforeConfirm(link) {
 *         $(link).css('background-color', 'red');
 *     },
 *     close(link) {
 *         $(link).css('background-color', '');
 *     },
 *     confirmation: "Are you so desperate that you want to remove me?",
 *     cancel: "Please, don't...",
 *     title: "Confirmation"
 * }, function (message, link) {
 *     if (message === 'allowed') {
 *	       $(link).remove();
 *	   }
 * });
 * </script>
 *
 * Example 2:
 * ----------
 * <a href="/removelink" data-confirmation="Are you 100% sure?" data-handler="removeMe">Try to remove me!</a>
 * <a href="/removelink" data-confirmation="Are you 100% sure?" data-handler="removeMeFake" data-title="Fake confirmation dialog">Try to remove me!</a>
 * <script type="text/javascript">
 * $('a[data-confirmation]').larchanaConfirmation({
 *     resizable: false,
 *     title: "Confirmation dialog"
 * }, {
 *     removeMe: function (message, link) {
 *         $(link).remove();
 *     },
 *     removeMeFake: function (message, link) {
 *         $(link).hide();
 *     }
 * });
 * </script>
 *
 * Options:
 * - bgiframe - see jQuery.UI.dialog options
 * - cancel - text for "Cancel" button (used only when the "data-cancel"
 *   attribute is not set)
 * - confirmation - confirmation text (used only when the "data-confirmation"
 *   attribute is not set)
 * - data - additional data to send to the handler function
 * - dialogClass - see jQuery.UI.dialog options
 * - handler - name of the handler - 'default', 'redirect', 'follow', null
 *   (used only when the "data-handler" attribute is not set)
 * - height - see jQuery.UI.dialog options
 * - redirect - address to redirect after confirmation (used only when
 *   the "data-redirect" attribute is not set)
 * - resizable - see jQuery.UI.dialog options
 * - title - title of the dialog (used only when the "data-title" attribute
 *   is not set)
 * - width - see jQuery.UI.dialog options
 *
 * Events:
 * - beforeConfirm(link) - triggered right after user clicks the link, before
 *   opening confirmation dialog
 * - close(link) - triggered when dialog failed to load or user pressed Esc
 *   or "Cancel" button
 * - execute(link, data) - called after user confirms the action, by default
 *   loads the link url with AJAX
 * - prepareData(link) - called before sending confirmation, by default
 *   serializes data from the dialog form
 *
 * Options used in html (data-* attributes)
 * - cancel (attribute "data-cancel")
 * - confirmation (attribute "data-confirmation")
 * - handler (attribute "data-handler")
 * - redirect (attribute "data-redirect")
 * - title (attribute "data-title")
 *
 */
(function ($, window) {
	/**
	 * Sets the link's click handler to show the confirmation dialog and
	 * perform the AJAX action when user confirms it
	 *
	 * @param {Object} options Plug-in options
	 * @param {function|Object} handlers Default handler function or list of user's handlers
	 * @return {jQuery} jQuery object
	 */
	$.fn.larchanaConfirmation = function larchanaConfirmation(options, handlers) {
		/**
		 * Gets the options either from link's data-* attribute or from plugin
		 * options
		 *
		 * @param {HTMLLinkElement} a Clicked link containing the data-* attributes
		 * @param {String} option Name of the option ("cancel", "confirmation", ...)
		 * @param {mixed} defaultValue Value returned when option is not found
		 * @return {mixed} Option value
		 *
		 */
		function getOption(a, option, defaultValue) {
			if (a.hasAttribute('data-' + option)) {
				return a.getAttribute('data-' + option);
			}

			if (options[option] !== undefined) {
				return options[option];
			}

			return defaultValue === undefined ? null : defaultValue;
		}


		/**
		 * Sends the AJAX POST request with the specified data
		 *
		 * @param {HTMLLinkElement} a Clicked link
		 * @param {mixed} data Data to send
		 *
		 */
		function execute(a, data) {
			var result = undefined;
			$.ajax({
				type: 'post',
				async: false,
				url: a.getAttribute('href'),
				data: data,
				complete: $.ajaxComplete(function (message) {
					result = message;
				})
			});
			return result;
		}

		/**
		 * Displays the confirmation dialog
		 *
		 * @param {HTMLLinkElement} a Clicked link
		 * @param {string} content HTML Content of the confrmation dialog
		 *
		 */
		function dialog(a, content) {
			var buttons = {};
			buttons[getOption(a, 'cancel', 'Cancel')] = function () {
				$('#dialogConfirmation').dialog('close');
			};
			buttons[$(a).text()] = function () {
				var data = typeof options.prepareData === 'function' ? options.prepareData(a) : $('#dialogConfirmation form').serialize(),
					result = typeof options.execute === 'function' ? options.execute(a, data) : execute(a, data);
				if (result !== undefined) {
					a.larchanaAjaxLinkHandlers[getOption(a, 'handler', 'default')](result, a, options.data);
					$('#dialogConfirmation').dialog('close');
				}
			};

			// Create the dialog
			//
			$('body').append('<div id="dialogConfirmation">' + content + '</div>');
			$('#dialogConfirmation').dialog({
				autoOpen: true,
				buttons: buttons,
				close: function () {
					if (typeof options.close === 'function') {
						options.close(a);
					}
					$('#dialogConfirmation').remove();
				},
				modal: true,
				title: getOption(a, 'title', $('#dialogConfirmation form').attr('data-title') ? $('#dialogConfirmation form').attr('data-title') : 'Confirmation'),

				bgiframe: options.bgiframe,
				dialogClass: options.dialogClass,
				height: options.height,
				resizable: options.resizable,
				width: options.width === undefined ? 'auto' : options.width
			});
		}

		/**
		 * Click handler - loads the dialog data with AJAX GET request
		 * and displays the confirmation dialog
		 *
		 */
		function click() {
			var a = this, content = getOption(a, 'confirmation', '');

			if (document.getElementById('dialogConfirmation')) {
				return false;
			}

			if (typeof options.beforeConfirm === 'function') {
				options.beforeConfirm(a);
			}

			// Load dialog content (if not set in the confirmation option)
			//
			if (content === '') {
				$('body').addClass('busy');
				$.ajax({
					type: 'get',
					url: a.getAttribute('href'),
					async: true,
					complete: $.ajaxComplete({
						success: function (message) {
							dialog(a, message);
						},
						error: function (message) {
							window.alert(message);
							if (typeof options.close === 'function') {
								options.close(a);
							}
						},
						complete: function () {
							$('body').removeClass('busy');
						}
					})
				});
			}
			else {
				dialog(a, content);
			}

			return false;
		}


		/**
		 * Creates default handlers:
		 * - redirect: redirects to data-redirect attribute value or to redirect option or reloads the document
		 * - follow: redirects to the link's href attribute
		 * - default: displays the alert or alert dialog if the returned message is an object with an 'html' property
		 *
		 * @return {Object} List of default handlers
		 * 
		 */
		function getDefaultHandlers() {
			return {
				'redirect': function (message, a, data) {
					window.document.location = getOption(a, 'redirect', message ? message : window.document.location);
				},
				'follow': function (message, a, data) {
					window.document.location = a.getAttribute('href');
				},
				'default': function (message, a, data) {
					if (typeof message === 'string') {
						window.alert(message);
					}
					else if (typeof message === 'object' && message.html !== undefined) {
						$('body').append('<div id="dialogAlert">' + message.html + '</div>');
						$('#dialogAlert').html(message.html).dialog({
							autoOpen: true,
							bgiframe: options.bgiframe,
							buttons: {
								'OK': function () {
									$('#dialogAlert').dialog('close');
								}
							},
							close: function () {
								$('#dialogAlert').remove();
							},
							height: 'auto',
							modal: true,
							resizable: false,
							width: 350
						});
					}
				}
			};
		}


		if (options === undefined) {
			options = {};
		}

		// Setup the handlers for each element (if not set before) and apply the click handler
		//
		this.each(function () {
			var i, firstRun = this.larchanaAjaxLinkHandlers === undefined;

			// Setup the default handlers
			//
			if (firstRun) {
				this.larchanaAjaxLinkHandlers = getDefaultHandlers();
			}

			// Apply default handler (used where data-handler is not defined or handler with this name is not defined
			//
			if (typeof handlers === 'function') {
				this.larchanaAjaxLinkHandlers['default'] = handlers;
			}
			// Default handler is not defined, use alert function and apply user defined named handlers
			//
			else if (typeof handlers === 'object') {
				for (i in handlers) {
					if (typeof handlers[i] === 'function') {
						this.larchanaAjaxLinkHandlers[i] = handlers[i];
					}
				}
			}

			// Create the default click handler (if not defined earlier)
			//
			if (firstRun) {
				$(this).click(click);
			}
		});

		return this;
	};
}(jQuery, window));
